package biz.yfsoft.app.fastframework.kit;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.MessageFormat;
import java.util.Date;

import biz.yfsoft.app.fastframework.bo.Bo;
import biz.yfsoft.app.fastframework.core.Fast;
import biz.yfsoft.app.fastframework.core.document.Document;
import biz.yfsoft.app.fastframework.kit.cmd.AbsCmdCallback;
import biz.yfsoft.app.fastframework.kit.cmd.CmdCallback;
import biz.yfsoft.app.fastframework.plugin.IFastPlugin;
import biz.yfsoft.app.fastframework.plugin.IFastPlugin.Status;

import com.alibaba.fastjson.JSONObject;
import com.jfinal.kit.PropKit;
import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Record;

public class DataKit {

	/**
	 * 将远程url的资源转换为字节数组
	 * @param url
	 * @return
	 */
	public static byte[] getRemoteFileData(String url){
		byte[] data = null;
		URL urlfile = null;
        HttpURLConnection httpUrl = null;
        BufferedInputStream bis = null;
        ByteArrayOutputStream bos = null;
        try
        {
            urlfile = new URL(url);
            httpUrl = (HttpURLConnection)urlfile.openConnection();
            httpUrl.connect();
            bis = new BufferedInputStream(httpUrl.getInputStream());
            bos = new ByteArrayOutputStream();
            //此处需要进行调试
            data = new byte[1024];
            int len = 0;
            while ((len = bis.read(data)) != -1)
            {
            	bos.write(data, 0, len); 
            }
            bis.close();
            bos.close();
            httpUrl.disconnect();
            data = bos.toByteArray();
        }
        catch (Exception e)
        {
        }
        finally
        {
        	if(bis!=null){
	            try{bis.close();}catch (IOException e){}
        	}
        	if(bos != null){
	            try{bos.close();}catch (IOException e){}
        	}
        }
		return data;
	}
	
	private static String _mysqlBinDir = null;
	
	private static JSONObject _mysqlConfig = null;
	
	private static JSONObject _bizConfig = null;
	
	private static JSONObject _ecConfig = null;
	/**
	 * 获取mysql客户端安装文件的bin目录
	 * @return
	 */
	public static String getMysqlBinDir(){
		if(StrKit.isBlank(_mysqlBinDir)){
			String binDir = PropKit.get("db.mysql.binDir");
			JSONObject binDirJson = JSONObject.parseObject(binDir);
			_mysqlBinDir = binDirJson.getString(RuntimeKit.currentOS.toString().toLowerCase());
		}
		return _mysqlBinDir;
	}
	
	/**
	 * 获取配置文件中的mysql的连接设置，通常包含
	 * "url":"jdbc:mysql://{0}:{1}/{2}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull","host":"192.168.31.88","port":"3306","name":"fast","user":"dbadmin","pass":"87252798"
	 * @return json格式的配置信息
	 */
	public static JSONObject getMysqlConfig(){
		if(null != _mysqlConfig)
			return _mysqlConfig;
		PropKit.use("config.txt");
		boolean devMode = PropKit.getBoolean("devMode", false);
		String connecter = PropKit.get("db.connectStr");
		JSONObject config = JSONObject.parseObject(connecter);
		_mysqlConfig = devMode?config.getJSONObject("dev"):config.getJSONObject("run"); 
		return _mysqlConfig;
	}
	
	/**
	 * 获取配置文件中的mysql的连接设置，通常包含
	 * "url":"jdbc:mysql://{0}:{1}/{2}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull","host":"192.168.31.88","port":"3306","name":"fast","user":"dbadmin","pass":"87252798"
	 * @return json格式的配置信息
	 */
	public static JSONObject getBizMysqlConfig(){
		if(null != _bizConfig)
			return _bizConfig;
		PropKit.use("config.txt");
		String connecter = PropKit.get("db.connectStr");
		JSONObject config = JSONObject.parseObject(connecter);
		_bizConfig = config.getJSONObject("biz"); 
		return _bizConfig;
	}
	
	/**
	 * 获取配置文件中的mysql的连接设置，通常包含
	 * "url":"jdbc:mysql://{0}:{1}/{2}?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull","host":"192.168.31.88","port":"3306","name":"fast","user":"dbadmin","pass":"87252798"
	 * @return json格式的配置信息
	 */
	public static JSONObject getEcMysqlConfig(){
		if(null != _ecConfig)
			return _ecConfig;
		PropKit.use("config.txt");
		String connecter = PropKit.get("db.connectStr");
		JSONObject config = JSONObject.parseObject(connecter);
		_ecConfig = config.getJSONObject("ec"); 
		return _ecConfig;
	}
	
	//导出mysql数据库的命令
	private static final String MYSQL_EXPORT_CMD = "{0}mysqldump --user={1} --port={2} --host={3} --password={4} --opt {5} > {6}";
	
	//导出mongodb数据库的命令
	private static final String MONGODB_EXPORT_CMD = "mongodump -h {0}:{1} -d {2} -o {3}";
	
	/**
	 * 进行mysql数据库的还原 
	 * 重要提醒：该函数执行会调用到当前机器的命令行来执行cmd命令，所以前提是一定要确定当前机器已经安装了mysql的客户端程序
	 * @param fileId 需要还原的备份文件的id
	 * @return
	 */
	public static CmdResult restoreMysql(int fileId){
		CmdResult result = new DataKit.CmdResult();
		
		//检查备份文件
		checkBackupFile(fileId,result);
		if(!result.flag) return result;
		
		//缓存中已经存在该备份文件了
		JSONObject config = getMysqlConfig();
		String user = config.getString("user");
		String pass = config.getString("pass");
		String host = config.getString("host");
		String port = config.getString("port");
		String name = config.getString("name");
		
		//获取到配置文件中的mysql的bin目录
		String binDir = getMysqlBinDir();
		
        String cmd = binDir+"mysql  -h{0} -P{1} -u{2} -p{3} {4} < {5}";
        cmd = MessageFormat.format(cmd, host,port,user,pass,name,result.filepath);
        try{
        	RuntimeKit.exec(cmd);
        	result.flag = true;
		} catch (Exception e) {
			result.flag = false;
			result.error = e.getMessage();
		}
        return result;
	}
	
	public static CmdResult restoreMongodb(int fileId){
		
		CmdResult result = new DataKit.CmdResult();
		IFastPlugin mongodbPlugin = checkMongodbPlugin(result);
		if(!result.flag) return result;
		
		//检查备份文件
		checkBackupFile(fileId,result);
		if(!result.flag) return result;
		
		String descDir = result.filepath;
		//解压下来
		if(result.filepath.endsWith(".bat")){
			descDir = result.filepath.substring(0,result.filepath.length()-4);
			File f = new File(descDir);
			if(f.exists()){
				f.delete();
			}
			FileKit.unZip(new File(result.filepath), f);
		}
		//获取插件初始化时候的配置，通常包含一些mongodb的连接信息
		JSONObject config = mongodbPlugin.getConfigJson();
		
		String host = config.getString("host");
		String port = config.getString("port");
		String name = config.getString("name");
		String bin = config.getString("bin");
		
		//加上 一定要有 --drop 参数
        String cmd = bin+"mongorestore --drop -h {0}:{1} --db {2} {3}"+File.separator+"{4}";
        cmd = MessageFormat.format(cmd, host,port,name,descDir,name);
        try{
        	System.out.println(cmd);
        	RuntimeKit.exec(cmd);
        	result.flag = true;
		} catch (Exception e) {
			result.flag = false;
			result.error = e.getMessage();
		}
        return result;
	}
	
	public static void checkBackupFile(int fileId,CmdResult result){
		Record record = Db.findById("sys_file", fileId);
		String sqlFile = record.getStr("path");
		result.flag = true;
		result.filepath = sqlFile;
		File f = new File(sqlFile);
		//查看本地缓存中是否包含该文件，否则需要通过远程下载该文件后进行操作
		if(!f.exists()){
			if(!Fast.me().hasPlugin("QiniuPlugin")){
				result.flag = false;
				result.error = "本地备份文件丢失，且未安装云文件存储的插件，请安装云文件存储插件之后，再进行重试!";
				return;
			}
			//未启动远程的云存储数据服务
			if(Status.ACTIVED != Fast.me().getPlugin("QiniuPlugin").getStatus()){
				result.flag = false;
				result.error = "本地备份文件丢失，请开启云文件存储的插件，再进行重试!";
				return;
			}
			//缓存文件不存在，则需要通过远程下载
			String hash = record.getStr("hash");
			try {
				Document.me().loadToLocal(hash, sqlFile);
			} catch (IOException e) {
				result.flag = false;
				result.error = e.getMessage();
			}
		}
	}
	
	public static IFastPlugin checkMongodbPlugin(CmdResult result){
		//判断mongodb插件是否安装
		if(!Fast.me().hasPlugin("MongodbPlugin")){
			result.error = "系统中的MongoDbPlugin插件未安装";
			result.flag = false;
			return null;
		}
		IFastPlugin mongodbPlugin = Fast.me().getPlugin("MongodbPlugin");
		//判断插件是否开启
		if(Status.ACTIVED!=mongodbPlugin.getStatus()){
			result.error = "系统中的MongoDbPlugin插件未开启";
			result.flag = false;
			return null;
		}
		result.flag = true;
		return mongodbPlugin;
	}
	
	public static CmdResult backupMongodb() {
		CmdResult result = new DataKit.CmdResult();
		IFastPlugin mongodbPlugin = checkMongodbPlugin(result);
		if(!result.flag) return result;
		//获取插件初始化时候的配置，通常包含一些mongodb的连接信息
		JSONObject config = mongodbPlugin.getConfigJson();
		
		String host = config.getString("host");
		String port = config.getString("port");
		String name = config.getString("name");
		String bin = config.getString("bin");
		
		//获取到缓存目录
		String dir = RuntimeKit.tempDir;
		//windows的缓存目录可能不是以目录分隔符结束的,需要添加上
		if(!dir.endsWith(File.separator)){
			dir = dir + File.separator;
		}
		//创建一个临时的目录
		//mongodb导出是以文件夹的形式保存的
		String bakDir = "bat_"+DateKit.toStr(new Date(), "yyyyMMddHHmmss");
		result.name = bakDir;
		
		String bakpath = dir + bakDir;
		result.filepath = bakpath;
		
		//拼接备份mysql的命令
        String cmd = bin+MessageFormat.format(MONGODB_EXPORT_CMD,host,port,name,bakpath);
        
        //需要进行目录的打包，所以需要在目录后面添加.zip
        Bo record = Document.me().prepar(result.name+".bat",result.filepath+".bat");
        //执行备份命令
        runCmd(cmd,new AbsCmdCallback(record) {
			
			@Override
			public void success() {
				Fast.AynTask(new Runnable(){
					@Override
					public void run() {
						String path = fRecord.getStr("path");
						String dir = path.replace(".bat", "");
						
						FileKit.zip(new File(dir),new File(path));
					}
				},10*1000L);
				super.success();
				
				result.flag = true;
			}
			
			@Override
			public void error(String error){
				result.flag = false;
				result.error = error;
			}
		});
        
        return result;
	}
	
	/**
	 * 备份mysql的函数
	 * 重要提醒：该函数执行会调用到当前机器的命令行来执行cmd命令，所以前提是一定要确定当前机器已经安装了mysql的客户端程序
	 * 同时，由于是通过mysql的导出命令操作的，不能保证导出的文件的i/o同步，所以通常要间隔一段时间才能对该文件进行i/o操作
	 * @return 导出的文件路径，是保存在数据缓存中的，可能会被清理，所以一般是要进行该文件的云存储的
	 * @throws RuntimeException
	 */
	public static CmdResult backupMysql() {
		CmdResult result = new DataKit.CmdResult();
		
		String binDir = getMysqlBinDir();
		
		JSONObject config = getMysqlConfig();
		
		String user = config.getString("user");
		String pass = config.getString("pass");
		String host = config.getString("host");
		String port = config.getString("port");
		String name = config.getString("name");
		
		//获取到缓存目录
		String dir = RuntimeKit.tempDir;
		//windows的缓存目录可能不是以目录分隔符结束的,需要添加上
		if(!dir.endsWith(File.separator)){
			dir = dir + File.separator;
		}
		//创建一个临时的文件名
		String bakFileName = "fast_{0}.bak";
		bakFileName = MessageFormat.format(bakFileName,DateKit.toStr(new Date(), "yyyyMMddHHmmss"));
		result.name = bakFileName;
		
		String bakpath = dir + bakFileName;
		result.filepath = bakpath;
		
		//拼接备份mysql的命令
        String cmd = MessageFormat.format(MYSQL_EXPORT_CMD,binDir, user,port,host,pass,name,bakpath);
        Bo record = Document.me().prepar(result.name,result.filepath);
        
        //执行备份命令
        runCmd(cmd,new AbsCmdCallback(record) {
			
			@Override
			public void success() {
				super.success();
				result.flag = true;
			}
			
			@Override
			public void error(String error){
				result.flag = false;
				result.error = error;
			}
		});
        
        return result;
	}
	
	public static void runCmd(String cmd,CmdCallback cb){
		try {
			System.out.println(cmd);
			RuntimeKit.exec(cmd);
			cb.success();
		} catch (Exception e) {
			e.printStackTrace();
			cb.error(e.getMessage());
		}
	}
	
	public final static class CmdResult{
		public String filepath;
		public String name;
		public boolean flag = false;
		public String error = "系统错误";
	}
}
